/*
 * @Author       : 邱狮杰
 * @Date         : 2021-06-23 13:53:28
 * @LastEditTime : 2021-06-23 14:11:02
 * @FilePath     : /rh-in-vue/src/core/useEffect.ts
 * @Description  : useEffect
 */
import { watch, onMounted, onBeforeUpdate, onBeforeUnmount, WatchSource } from 'vue'
import type {} from '@vue/runtime-core'

export type baseCb = () => void
export type withDestroyCb = () => () => void
export type Dep = (WatchSource | object)[]
/**
 * @description 模拟react的Effect唯一的不同是改函数是同步执行的,return函数内获取的值也是当前的状态不是prev的值
 * @see https://react.docschina.org/docs/hooks-reference.html#useeffect
 * @param { baseCb | withDestroyCb } cb
 * @param { undefined | null | [] | unknown[]  } deps
 * @return { void }
 */
function useEffect(cb: baseCb | withDestroyCb, deps: undefined): void
function useEffect(cb: baseCb | withDestroyCb, deps: []): void
function useEffect(cb: withDestroyCb, deps: unknown[]): void
function useEffect(cb: baseCb, deps: unknown[]): void
function useEffect(cb: baseCb, deps: WatchSource): void
function useEffect(cb: baseCb, deps: Dep): void
function useEffect(cb: unknown, deps: unknown): void {
  let backupCb: null | baseCb = null
  onMounted(async () => {
    backupCb = typeof cb === 'function' && (await cb())
  })
  onBeforeUnmount(async () => {
    backupCb && (await backupCb())
  })
  if (!deps) {
    //  undefined | null
    onBeforeUpdate(() => {
      typeof cb === 'function' && cb()
    })
    return
  }
  // 没有依赖的情况下
  if (!(deps as []).length) return
  watch(deps as Dep, () => {
    typeof cb === 'function' && cb()
  })
}

export { useEffect }
